﻿using K9Nano.Updater.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace K9Nano.Updater.Server.Authorization;

public class K9HashAuthorizationHandler : IAuthorizationHandler
{
    private readonly ILogger _logger;

    public K9HashAuthorizationHandler(ILogger<K9HashAuthorizationHandler> logger)
    {
        _logger = logger;
    }

    public async Task<bool> AuthorizeAsync(HttpContext httpContext)
    {
        var clientId = httpContext.Request.Headers[Constants.HeaderClientId].FirstOrDefault();

        if (string.IsNullOrEmpty(clientId))
        {
            _logger.LogError("无法获取请求端的ClientId");
            return false;
        }

        var sign = httpContext.Request.Headers[Constants.HeaderSign].FirstOrDefault();

        if (string.IsNullOrEmpty(sign))
        {
            _logger.LogError("无法获取请求端的Sign：{clientId}", clientId);
            return false;
        }

        var timeStampText = httpContext.Request.Headers[Constants.HeaderTimeStamp].FirstOrDefault();

        if (string.IsNullOrEmpty(timeStampText) || !long.TryParse(timeStampText, out var timeStamp))
        {
            _logger.LogError("时间戳无效：{clientId}", clientId);
            return false;
        }

        var path = httpContext.Request.Path;

        var clientStore = httpContext.RequestServices.GetRequiredService<IClientStore>();
        var secret = await clientStore.GetSecretAsync(clientId, httpContext.RequestAborted);
        if (string.IsNullOrEmpty(secret))
        {
            _logger.LogError("ClientId无效：{clientId}", clientId);
            return false;
        }

        var text = string.Format(Constants.SignTemplate, clientId, secret, timeStamp, path);
        var hash = HashUtils.Md5(text);

        if (!sign.Equals(hash, StringComparison.Ordinal))
        {
            _logger.LogError("签名不正确：{clientId}", clientId);
            _logger.LogDebug("待签名字符串：{text}", text);
            return false;
        }

        var options = httpContext.RequestServices.GetRequiredService<IOptions<K9UpdateOptions>>().Value;

        if (options.ClockSkew > 0
            && DateTimeOffset.UtcNow.ToUnixTimeSeconds() - timeStamp > options.ClockSkew)
        {
            _logger.LogError("签名已经过期：{clientId}", clientId);
            return false;
        }

        var endpoint = httpContext.GetEndpoint();
        if (endpoint is null)
        {
            _logger.LogError("无法获取请求节点信息");
            return false;
        }

        return true;
    }
}
